Layout

1. Legend

set.seed(123)
x <- 1:100
y1 <- 2 * x + rnorm(100)
y2 <- -2 * x + rnorm(100)

plot_ly(x = x, y = y1, type = "scatter") %>%
  add_trace(x = x, y = y2) %>%
  layout(legend = list(
    # x,y means where to put the legend box
    x = 1, y = 0.5, bgcolor = "#ebebeb"
  ))

2. Axes

set.seed(123)
x <- 1:100
y1 <- 2 * x + rnorm(100)
y2 <- -2 * x + rnorm(100)

# axis config object
axis_tempalte <- list(
  showgrid = F,
  zeroline = F,
  nticks = 20,
  showline = T,
  title = "AXIS",
  mirror = "all"
)

plot_ly(x = x, y = y1, type = "scatter") %>%
  layout(xaxis = axis_tempalte, yaxis = axis_tempalte)

Subplots

subplot(
  plot_ly(mtcars, x = ~mpg, y = ~qsec, name = "default"),
  plot_ly(mtcars, x = ~mpg, y = ~qsec, name = "alpha"),
  plot_ly(mtcars, x = ~mpg, y = ~wt)
)

Parameters

plot_ly()

  • @param {data.frame} data
  • @param {vector} x, y, z
  • @param {string} type
  • @param {string} mode
  • @param {vector} color, text, size
  • @param {string or vector} colorscale
  • @param {list} marker
    • @name {string} color
    • @name {list} symbol
  • @param {list} line
    • @name {string} color
    • @name {number} width

add_trace()

  • @param {list}

layout()

https://plotly.com/r/reference/#Layout_and_layout_style_objects

  • @param {string} title
  • @param {list} xaxis, yaxis
  • @param {list} scene
    • @name {list} xaxis, yaxis, zaxis
  • @param {list} geo
  • @param {list} legend
  • @param {list} annotations

Turn ggplot2 to plotly

ggplotly()可以将ggplot2图对象转化为 plotly 图对象,然后使用plotly的API操作

mydata <- data.frame(diamonds)
mydata <- mydata[sample(nrow(mydata), 500), ]
head(mydata)
#>       carat     cut color clarity depth table price    x    y    z
#> 8983   1.19   Ideal     J     SI2  61.7    56  4508 6.80 6.85 4.21
#> 37769  0.41   Ideal     E     VS2  62.1    57   999 4.79 4.74 2.96
#> 29479  0.40 Premium     G     SI1  61.4    60   702 4.67 4.71 2.88
#> 5407   1.00    Good     E     SI2  63.7    61  3819 6.27 6.32 4.01
#> 44617  0.53   Ideal     G     VS2  60.1    54  1607 5.31 5.30 3.19
#> 32634  0.27   Ideal     I    VVS2  62.1    55   459 4.17 4.20 2.60
ggplot(mydata, aes(carat, price, colour = color)) +
  geom_point()

ggplotly()
p <- ggplot(mydata, aes(carat, price, colour = color)) +
  geom_point() +
  theme_economist() +
  scale_colour_economist() +
  facet_wrap(~cut)
class(p)
#> [1] "gg"     "ggplot"
p2 <- ggplotly(p)
class(p2)
#> [1] "plotly"     "htmlwidget"
p2
LS0tDQp0aXRsZTogIlBsb3RseSBEZW1vIg0Kc3VidGl0bGU6ICcnDQphdXRob3I6ICJIdW1vb24iDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IGh0bWxfZG9jdW1lbnQNCmRvY3VtZW50Y2xhc3M6IGN0ZXhhcnQNCmNsYXNzb3B0aW9uOiBoeXBlcnJlZiwNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQ0Kc291cmNlKCIuL3NyYy9SbWFya2Rvd25fY29uZmlnLlIiKQ0KDQojIyBnbG9iYWwgb3B0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3aWR0aCA9IGNvbmZpZyR3aWR0aCwNCiAgZmlnLndpZHRoID0gY29uZmlnJGZpZy53aWR0aCwNCiAgZmlnLmFzcCA9IGNvbmZpZyRmaWcuYXNwLA0KICBvdXQud2lkdGggPSBjb25maWckb3V0LndpZHRoLA0KICBmaWcuYWxpZ24gPSBjb25maWckZmlnLmFsaWduLA0KICBmaWcucGF0aCA9IGNvbmZpZyRmaWcucGF0aCwNCiAgZmlnLnNob3cgPSBjb25maWckZmlnLnNob3csDQogIHdhcm4gPSBjb25maWckd2FybiwNCiAgd2FybmluZyA9IGNvbmZpZyR3YXJuaW5nLA0KICBtZXNzYWdlID0gY29uZmlnJG1lc3NhZ2UsDQogIGVjaG8gPSBjb25maWckZWNobywNCiAgZXZhbCA9IGNvbmZpZyRldmFsLA0KICB0aWR5ID0gY29uZmlnJHRpZHksDQogIGNvbW1lbnQgPSBjb25maWckY29tbWVudCwNCiAgY29sbGFwc2UgPSBjb25maWckY29sbGFwc2UsDQogIGNhY2hlID0gY29uZmlnJGNhY2hlLA0KICBjYWNoZS5jb21tZW50cyA9IGNvbmZpZyRjYWNoZS5jb21tZW50cywNCiAgYXV0b2RlcCA9IGNvbmZpZyRhdXRvZGVwDQopDQpgYGANCg0KDQojIyBHYWxsZXJ5DQoNCg0KIyMjIDEuIExpbmUgUGxvdA0KDQpgYGB7cn0NCnBsb3RfbHkoDQogIHggPSBjKDEsIDIsIDMpLA0KICB5ID0gYyg1LCA3LCA4KSwNCiAgdHlwZSA9ICJzY2F0dGVyIiwNCiAgbW9kZSA9ICJsaW5lcyINCikNCmBgYA0KDQoNCiMjIyAyLiBTY2F0dGVyIFBsb3QNCmBgYHtyfQ0KcGxvdF9seSgNCiAgeCA9IGMoMSwgMiwgMyksDQogIHkgPSBjKDUsIDcsIDgpLA0KICB0eXBlID0gInNjYXR0ZXIiLA0KICBtb2RlID0gIm1hcmtlcnMiLA0KICBzaXplID0gYygyLCA1LCAxMCksDQogIG1hcmtlciA9IGxpc3QoY29sb3IgPSBjKCJyZWQiLCAiYmx1ZSIsICJncmVlbiIpKQ0KKQ0KDQpwbG90X2x5KA0KICBkYXRhID0gaXJpcywNCiAgeCA9IH5QZXRhbC5MZW5ndGgsDQogIHkgPSB+UGV0YWwuV2lkdGgsDQogIGNvbG9yID0gflNwZWNpZXMsDQogIHR5cGUgPSAic2NhdHRlciIsDQogIG1vZGUgPSAibWFya2VycyINCikNCmBgYA0KDQojIyMgMy4gQmFyIENoYXJ0DQoNCuadoeW9ouWbvuaoqui9tOm7mOiupOaMieWtl+avjeaOkuW6j++8jOimgeWKoOWFpWBjYXRlZ29yeW9yZGVyID0gImFycmF5ImDpgInpobnvvIzmiY3kvJrmjInnu5nlrprnmoTpobrluo/mjpLluo8NCg0KYGBge3J9DQpwbG90X2x5KA0KICB4ID0gYygiQyIsICJCIiwgIkEiKSwNCiAgeSA9IGMoNSwgMTAsIDgpLA0KICB0eXBlID0gImJhciINCikgJT4lDQogIGxheW91dCh4YXhpcyA9IGxpc3QoY2F0ZWdvcnlhcnJheSA9IH5uYW1lcywgY2F0ZWdvcnlvcmRlciA9ICJhcnJheSIpKQ0KYGBgDQoNCiMjIyA0LiBIZWF0bWFwDQoNCmBgYHtyfQ0KY2xhc3Modm9sY2FubykNCg0Kc3RyKHZvbGNhbm8pDQoNCnBsb3RfbHkoeiA9IHZvbGNhbm8sIHR5cGUgPSAiaGVhdG1hcCIpDQoNCiMg6Iul55+p6Zi155qE6KGM5YiX5Z2H5Li65YiG57G75Y+Y6YeP77yM5YiZ6ZyA6KaB5qCH5YeG55+p6Zi155qE6KGM44CB5YiX5ZCNDQojIHBsb3RfbHkoDQojICAgeCA9IHJvd25hbWVzKG1haW5faW50aW1hdGVfbWF0cml4KSwNCiMgICB5ID0gY29sbmFtZXMobWFpbl9pbnRpbWF0ZV9tYXRyaXgpLA0KIyAgIHogPSBtYWluX2ludGltYXRlX21hdHJpeCwNCiMgICB0eXBlID0gImhlYXRtYXAiDQojICkNCmBgYA0KDQojIyMgNS4gQXJlYSBQbG90DQpgYGB7cn0NCnBsb3RfbHkoDQogIHggPSBjKDEsIDIsIDMpLA0KICB5ID0gYyg1LCA2LCA4KSwNCiAgdHlwZSA9ICJzY2F0dGVyIiwNCiAgbW9kZSA9ICJsaW5lcyIsDQogIGZpbGwgPSAidG96ZXJveSIgIyBmcm9tIGxpbmUgZG93biB0byB4IGF4aXMNCikNCmBgYA0KDQoNCiMjIyA2LiBIaXN0b2dyYW0NCg0KYGBge3J9DQp4IDwtIHJjaGlzcSgxMDAsIDUsIDApDQp4DQoNCnBsb3RfbHkoeCA9IHgsIHR5cGUgPSAiaGlzdG9ncmFtIikNCmBgYA0KDQojIyMgNy4gQm94IFBsb3QNCmBgYHtyfQ0KcGxvdF9seSh5ID0gcm5vcm0oNTApLCB0eXBlID0gImJveCIpICU+JQ0KICBhZGRfdHJhY2UoeSA9IHJub3JtKDUwLCAxKSkNCg0KcGxvdF9seShtaWR3ZXN0LCB4ID0gfnBlcmNvbGxlZ2UsIGNvbG9yID0gfnN0YXRlLCB0eXBlID0gImJveCIpDQpgYGANCg0KIyMjIDguIDJEIEhpc3RvZ3JhbQ0KYGBge3J9DQpwbG90X2x5KA0KICB4ID0gcm5vcm0oMTAwMCwgc2QgPSAxMCksDQogIHkgPSBybm9ybSgxMDAwLCBzZCA9IDUpLA0KICB0eXBlID0gImhpc3RvZ3JhbTJkIg0KKQ0KYGBgDQoNCiMjIyA5LiBCdWJibGUgTWFwDQpgYGB7cn0NCnBsb3RfbHkoDQogIHR5cGUgPSAic2NhdHRlcmdlbyIsDQogIGxvbiA9IGMoLTczLjUsIDE1MS4yKSwNCiAgbGF0ID0gYyg0NS41LCAtMzMuOCksDQogIG1hcmtlciA9IGxpc3QoY29sb3IgPSBjKCJyZWQiLCAiYmx1ZSIpLCBzaXplID0gYygzMCwgNTApLCBtb2RlID0gIm1hcmtlcnMiKQ0KKQ0KYGBgDQojIyMgMTAuIENob3JvcGxldGggTWFwDQoNCmBgYHtyfQ0KcGxvdF9seSgNCiAgdHlwZSA9ICJjaG9yb3BsZXRoIiwNCiAgbG9jYXRpb25zID0gYygiQVoiLCAiQ0EiLCAiVlQiKSwNCiAgbG9jYXRpb25tb2RlID0gIlVTQS1zdGF0ZXMiLA0KICBjb2xvcnNjYWxlID0gIlZpcmlkaXMiLA0KICB6ID0gYygxMCwgMjAsIDQwKQ0KKSAlPiUNCiAgbGF5b3V0KGdlbyA9IGxpc3Qoc2NvcGUgPSAidXNhIikpDQpgYGANCg0KIyMjIDExLiBTY2F0dGVyIE1hcA0KDQpgYGB7cn0NCnBsb3RfbHkoDQogIHR5cGUgPSAic2NhdHRlcmdlbyIsDQogIGxvbiA9IGMoNDIsIDM5KSwNCiAgbGF0ID0gYygxMiwgMjIpLA0KICB0ZXh0ID0gYygiUm9tZSIsICJHcmVlY2UiKSwNCiAgbW9kZSA9ICJtYXJrZXJzIg0KKQ0KYGBgDQoNCiMjIyAxMi4gM0QgU3VyZmFjZSBQbG90DQoNCi0gdHlwZTpgc3VyZmFjZWDvvIwNCi0gejpgfm1hdHJpeGANCi0g5rOo5oSP77yM5aaC5p6c6KaB5oyH5a6aIHgg5ZKMIHkg6L2077yM5b+F6aG755SoIGB6ID0gb3V0ZXIoeCwgeSwgLmYpICU+JSB0KClg77yM5LiA5a6a6KaB6L2s572u77yBDQoNCg0KYGBge3J9DQojIOS4jeaMh+Wumnjlkox55pe277yMM0Qgc3VyZmFjZeWbvueahHjovbTlkox56L205YiG5Yir5piv55+p6Zi15YiX5ZKM6KGM55qEaW5kZXjvvIzku44w5byA5aeL6Ze06ZqUMeiuoeeulw0KcGxvdF9seSh0eXBlID0gInN1cmZhY2UiLCB6ID0gfnZvbGNhbm8pDQoNCiMg5oyH5a6aeOWSjHnnmoTmoIflh4YzROabsumdoueUn+aIkOa1geeoi++8muWFs+mUruWHveaVsCBvdXRlcih4LCB5LCAuZikgJT4lIHQoKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoaHRtbHdpZGdldHMpDQoNCnNjYWxlIDwtIDEwMDANCg0Kc2lnbW9pZCA8LSBmdW5jdGlvbih4KSB7DQogIDEgLyAoMSArIGV4cCgteCkpDQp9DQoNCmNhbF96IDwtIGZ1bmN0aW9uKHgsIHkpIHsNCiAgMTAgKiB4IC0gMTUgKiB5DQp9DQoNCmNhbF9hIDwtIGZ1bmN0aW9uKHgsIHkpIHsNCiAgY2FsX3ooeCwgeSkgJT4lIHNpZ21vaWQoKQ0KfQ0KDQoNCngxIDwtIHJ1bmlmKHNjYWxlKSAlPiUgc29ydCgpDQp5MSA8LSBydW5pZihzY2FsZSkgJT4lIHNvcnQoKQ0KejEgPC0gb3V0ZXIoeDEsIHkxLCBjYWxfYSkgJT4lIHQoKQ0KcDEgPC0gcGxvdF9seSh0eXBlID0gInN1cmZhY2UiLCB4ID0geDEsIHkgPSB5MSwgeiA9IH56MSkNCnNhdmVXaWRnZXQocDEsICIuLi9maWd1cmUvc2lnbW9pZOabsumdojEuaHRtbCIsIHNlbGZjb250YWluZWQgPSBGLCBsaWJkaXIgPSAibGliIikNCnAxDQoNCngyIDwtIHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gc2NhbGUgKyAxKQ0KeTIgPC0gc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSBzY2FsZSArIDEpDQp6MiA8LSBvdXRlcih4MiwgeTIsIGNhbF9hKSAlPiUgdCgpDQpwMiA8LSBwbG90X2x5KHR5cGUgPSAic3VyZmFjZSIsIHggPSB4MiwgeSA9IHkyLCB6ID0gfnoyKQ0Kc2F2ZVdpZGdldChwMiwgIi4uL2ZpZ3VyZS9zaWdtb2lk5puy6Z2iMi5odG1sIiwgc2VsZmNvbnRhaW5lZCA9IEYsIGxpYmRpciA9ICJsaWIiKQ0KcDINCmBgYA0KDQoNCmBgYHtyfQ0KIyMgY29uZmlnID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQp4IDwtIHNlcSgwLCAxMDAsIDUpDQp5IDwtIHNlcSgxMDAsIDIwMCwgNSkNCnBhcmFtcyA8LSAxOjUgKiAxMA0KDQojIyBwbG90dGluZyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyDlm77nmoTmoYbmnrYNCnAwIDwtIHBsb3RfbHkodHlwZSA9ICJzdXJmYWNlIiwgc2hvd3NjYWxlID0gRikNCg0KcCA8LSBwYXJhbXMgJT4lDQogICMgMS4g5Y+C5pWw55Sx5Ye95pWw5bel5Y6C5Yqg5bel5oiQ5Ye95pWwbGlzdA0KICBtYXAofiBmdW5jdGlvbihhLCBiKSAwLjAyICogYSArIDAuMDE1ICogYiAtIDAuMDAwOCAqIGEgKiBiICsgMC4wMDA3ICogYV4yIC0gMC4wMDAyICogYl4yICsgLikgJT4lDQogICMgMi4g5Ye95pWwbGlzdOS9nOeUqOS6jngsIHksIOW+l+WIsCBtYXRyaXggbGlzdA0KICBtYXAofiBvdXRlcih4LCB5LCAuKSAlPiUgdCgpKSAlPiUNCiAgIyAzLiDmiYDmnInnmoQgbWF0cml4IOS+neasoeS9nOS4uuaIqumdou+8jOWPoOWKoOWIsCBwMOS4ig0KICByZWR1Y2UoDQogICAgLmYgPSBmdW5jdGlvbihwLCBtKSBwICU+JSBhZGRfc3VyZmFjZSh4ID0geCwgeSA9IHksIHogPSB+bSksDQogICAgLmluaXQgPSBwMA0KICApDQpwDQpgYGANCg0KDQojIyMgMTMuIDNEIExpbmUgUGxvdA0KDQoNCi0gdHlwZTpgc2NhdHRlcjNkYO+8jA0KLSBtb2RlOmBsaW5lc2ANCg0KYGBge3J9DQpwbG90X2x5KA0KICB0eXBlID0gInNjYXR0ZXIzZCIsDQogIHggPSBjKDksIDgsIDUsIDEpLA0KICB5ID0gYygxLCAyLCA0LCA4KSwNCiAgeiA9IGMoMTEsIDgsIDE1LCAzKSwNCiAgbW9kZSA9ICJsaW5lcyINCikNCmBgYA0KDQojIyMgMTQuIDNEIFNjYXR0ZXIgUGxvdA0KDQoNCi0gdHlwZTpgc2NhdHRlcjNkYO+8jA0KLSBtb2RlOmBtYXJrZXJzYA0KDQpgYGB7cn0NCnBsb3RfbHkoDQogIHR5cGUgPSAic2NhdHRlcjNkIiwNCiAgeCA9IGMoOSwgOCwgNSwgMSksDQogIHkgPSBjKDEsIDIsIDQsIDgpLA0KICB6ID0gYygxMSwgOCwgMTUsIDMpLA0KICBtb2RlID0gIm1hcmtlcnMiDQopDQpgYGANCg0KIyMgTGF5b3V0DQoNCiMjIyAxLiBMZWdlbmQNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQp4IDwtIDE6MTAwDQp5MSA8LSAyICogeCArIHJub3JtKDEwMCkNCnkyIDwtIC0yICogeCArIHJub3JtKDEwMCkNCg0KcGxvdF9seSh4ID0geCwgeSA9IHkxLCB0eXBlID0gInNjYXR0ZXIiKSAlPiUNCiAgYWRkX3RyYWNlKHggPSB4LCB5ID0geTIpICU+JQ0KICBsYXlvdXQobGVnZW5kID0gbGlzdCgNCiAgICAjIHgseSBtZWFucyB3aGVyZSB0byBwdXQgdGhlIGxlZ2VuZCBib3gNCiAgICB4ID0gMSwgeSA9IDAuNSwgYmdjb2xvciA9ICIjZWJlYmViIg0KICApKQ0KYGBgDQoNCiMjIyAyLiBBeGVzDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KeCA8LSAxOjEwMA0KeTEgPC0gMiAqIHggKyBybm9ybSgxMDApDQp5MiA8LSAtMiAqIHggKyBybm9ybSgxMDApDQoNCiMgYXhpcyBjb25maWcgb2JqZWN0DQpheGlzX3RlbXBhbHRlIDwtIGxpc3QoDQogIHNob3dncmlkID0gRiwNCiAgemVyb2xpbmUgPSBGLA0KICBudGlja3MgPSAyMCwNCiAgc2hvd2xpbmUgPSBULA0KICB0aXRsZSA9ICJBWElTIiwNCiAgbWlycm9yID0gImFsbCINCikNCg0KcGxvdF9seSh4ID0geCwgeSA9IHkxLCB0eXBlID0gInNjYXR0ZXIiKSAlPiUNCiAgbGF5b3V0KHhheGlzID0gYXhpc190ZW1wYWx0ZSwgeWF4aXMgPSBheGlzX3RlbXBhbHRlKQ0KYGBgDQoNCiMjIyBTdWJwbG90cw0KDQpgYGB7cn0NCnN1YnBsb3QoDQogIHBsb3RfbHkobXRjYXJzLCB4ID0gfm1wZywgeSA9IH5xc2VjLCBuYW1lID0gImRlZmF1bHQiKSwNCiAgcGxvdF9seShtdGNhcnMsIHggPSB+bXBnLCB5ID0gfnFzZWMsIG5hbWUgPSAiYWxwaGEiKSwNCiAgcGxvdF9seShtdGNhcnMsIHggPSB+bXBnLCB5ID0gfnd0KQ0KKQ0KYGBgDQoNCiMjIFBhcmFtZXRlcnMNCg0KIyMjIyBgcGxvdF9seSgpYA0KDQotIEBwYXJhbSB7ZGF0YS5mcmFtZX0gZGF0YQ0KLSBAcGFyYW0ge3ZlY3Rvcn0geCwgeSwgeg0KLSBAcGFyYW0ge3N0cmluZ30gdHlwZSANCi0gQHBhcmFtIHtzdHJpbmd9IG1vZGUNCi0gQHBhcmFtIHt2ZWN0b3J9IGNvbG9yLCB0ZXh0LCBzaXplDQotIEBwYXJhbSB7c3RyaW5nIG9yIHZlY3Rvcn0gY29sb3JzY2FsZQ0KLSBAcGFyYW0ge2xpc3R9IG1hcmtlcg0KICAtIEBuYW1lIHtzdHJpbmd9IGNvbG9yDQogIC0gQG5hbWUge2xpc3R9IHN5bWJvbA0KLSBAcGFyYW0ge2xpc3R9IGxpbmUNCiAgLSBAbmFtZSB7c3RyaW5nfSBjb2xvcg0KICAtIEBuYW1lIHtudW1iZXJ9IHdpZHRoDQoNCg0KIyMjIyBgYWRkX3RyYWNlKClgDQoNCi0gQHBhcmFtIHtsaXN0fQ0KDQojIyMjIGBsYXlvdXQoKWANCg0KaHR0cHM6Ly9wbG90bHkuY29tL3IvcmVmZXJlbmNlLyNMYXlvdXRfYW5kX2xheW91dF9zdHlsZV9vYmplY3RzDQoNCi0gQHBhcmFtIHtzdHJpbmd9IHRpdGxlDQotIEBwYXJhbSB7bGlzdH0geGF4aXMsIHlheGlzDQotIEBwYXJhbSB7bGlzdH0gc2NlbmUNCiAgLSBAbmFtZSB7bGlzdH0geGF4aXMsIHlheGlzLCB6YXhpcw0KLSBAcGFyYW0ge2xpc3R9IGdlbw0KLSBAcGFyYW0ge2xpc3R9IGxlZ2VuZA0KLSBAcGFyYW0ge2xpc3R9IGFubm90YXRpb25zDQoNCg0KIyMgVHVybiBnZ3Bsb3QyIHRvIHBsb3RseQ0KDQpgZ2dwbG90bHkoKWDlj6/ku6XlsIZnZ3Bsb3Qy5Zu+5a+56LGh6L2s5YyW5Li6IHBsb3RseSDlm77lr7nosaHvvIznhLblkI7kvb/nlKhwbG90bHnnmoRBUEnmk43kvZwNCg0KYGBge3J9DQpteWRhdGEgPC0gZGF0YS5mcmFtZShkaWFtb25kcykNCm15ZGF0YSA8LSBteWRhdGFbc2FtcGxlKG5yb3cobXlkYXRhKSwgNTAwKSwgXQ0KaGVhZChteWRhdGEpDQoNCmdncGxvdChteWRhdGEsIGFlcyhjYXJhdCwgcHJpY2UsIGNvbG91ciA9IGNvbG9yKSkgKw0KICBnZW9tX3BvaW50KCkNCmdncGxvdGx5KCkNCg0KcCA8LSBnZ3Bsb3QobXlkYXRhLCBhZXMoY2FyYXQsIHByaWNlLCBjb2xvdXIgPSBjb2xvcikpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgdGhlbWVfZWNvbm9taXN0KCkgKw0KICBzY2FsZV9jb2xvdXJfZWNvbm9taXN0KCkgKw0KICBmYWNldF93cmFwKH5jdXQpDQpjbGFzcyhwKQ0KDQpwMiA8LSBnZ3Bsb3RseShwKQ0KY2xhc3MocDIpDQpwMg0KYGBgDQoNCg==